﻿namespace VK.Lib
{

	/// <summary>
	/// Represents the authentication result of VK.
	/// </summary>
	public class VKOAuthResult
	{

		/// <summary>
		/// Initializes a new instance of the <see cref="VKOAuthResult"/> class.
		/// </summary>
		protected VKOAuthResult()
		{
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="VKOAuthResult"/> class.
		/// </summary>
		/// <param name="parameters">
		/// The parameters.
		/// </param>
		/// <remarks>
		/// The values of parameters should not be url encoded.
		/// </remarks>
		internal VKOAuthResult(System.Collections.Generic.IDictionary<string, object> parameters)
		{
			if (parameters == null)
				throw new System.ArgumentNullException("parameters");

			if (parameters.ContainsKey("error"))
			{
				_error = parameters["error"].ToString();

				if (parameters.ContainsKey("error_reason"))
				{
					_errorReason = parameters["error_reason"].ToString();
				}

				if (parameters.ContainsKey("error_description"))
				{
					_errorDescription = parameters["error_description"].ToString();
				}

				return;
			}

			if (parameters.ContainsKey("access_token"))
			{
				_accessToken = parameters["access_token"].ToString();
			}

			if (parameters.ContainsKey("user_id"))
			{
				_userID = parameters["user_id"].ToString();
			}

			if (parameters.ContainsKey("expires_in"))
			{
				var expiresIn = System.Convert.ToDouble(parameters["expires_in"]);
				_expires = expiresIn == 0 ? System.DateTime.MaxValue : System.DateTime.UtcNow.AddSeconds(expiresIn);
			}
		}

		/// <summary>
		/// The access token.
		/// </summary>
		private readonly string _accessToken;

		/// <summary>
		/// The user id.
		/// </summary>
		private readonly string _userID;

		/// <summary>
		/// Date and Time when the access token expires.
		/// </summary>
		private readonly System.DateTime _expires;

		/// <summary>
		/// Error that happens when using OAuth2 protocol.
		/// </summary>
		private readonly string _error;

		/// <summary>
		/// Short error reason for failed authentication if there was an error.
		/// </summary>
		private readonly string _errorReason;

		/// <summary>
		/// Long error description for failed authentication if there was an error.
		/// </summary>
		private readonly string _errorDescription;

		/// <summary>
		/// Gets the <see cref="DateTime"/> when the access token will expire.
		/// </summary>
		public virtual System.DateTime Expires
		{
			get { return _expires; }
		}

		/// <summary>
		/// Gets the access token.
		/// </summary>
		public virtual string AccessToken
		{
			get { return _accessToken; }
		}

		/// <summary>
		/// Gets the user id.
		/// </summary>
		public virtual string UserID
		{
			get { return _userID; }
		}

		/// <summary>
		/// Error that happens when using OAuth2 protocol.
		/// </summary>
		public virtual string Error
		{
			get { return _error; }
		}

		/// <summary>
		/// Gets the short error reason for failed authentication if an error occurred.
		/// </summary>
		public virtual string ErrorReason
		{
			get { return _errorReason; }
		}

		/// <summary>
		/// Gets the long error description for failed authentication if an error occurred.
		/// </summary>
		public virtual string ErrorDescription
		{
			get { return _errorDescription; }
		}

		/// <summary>
		/// Gets a value indicating whether access token or code was successfully retrieved.
		/// </summary>
		public virtual bool IsSuccess
		{
			get
			{
				return string.IsNullOrEmpty(Error) && !string.IsNullOrEmpty(AccessToken);
			}
		}

		/// <summary>
		/// Parse the url to <see cref="VKOAuthResult"/>.
		/// </summary>
		/// <param name="uriString">
		/// The url to parse.
		/// </param>
		/// <returns>
		/// Returns an instance of <see cref="VKOAuthResult"/>.
		/// </returns>
		public static VKOAuthResult Parse(string uriString)
		{
			return Parse(new System.Uri(uriString));
		}

		/// <summary>
		/// Parse the url to <see cref="VKOAuthResult"/>.
		/// </summary>
		/// <param name="uri">
		/// The url to parse.
		/// </param>
		/// <returns>
		/// Returns an instance of <see cref="VKOAuthResult"/>.
		/// </returns>
		public static VKOAuthResult Parse(System.Uri uri)
		{
			return Parse(uri, true);
		}

		/// <summary>
		/// Try parsing the url to <see cref="VKOAuthResult"/>.
		/// </summary>
		/// <param name="uriString">
		/// The url to parse.
		/// </param>
		/// <param name="result">
		/// An instance of <see cref="VKOAuthResult"/>.
		/// </param>
		/// <returns>
		/// Returns true if parsing was successful otherwise false.
		/// </returns>
		public static bool TryParse(string uriString, out VKOAuthResult result)
		{
			if (System.Uri.IsWellFormedUriString(uriString, System.UriKind.RelativeOrAbsolute))
				return TryParse(new System.Uri(uriString), out result);

			result = null;
			return false;
		}

		/// <summary>
		/// Try parsing the url to <see cref="VKOAuthResult"/>.
		/// </summary>
		/// <param name="uri">
		/// The url to parse.
		/// </param>
		/// <param name="result">
		/// An instance of <see cref="VKOAuthResult"/>.
		/// </param>
		/// <returns>
		/// Returns true if parsing was successful otherwise false.
		/// </returns>
		public static bool TryParse(System.Uri uri, out VKOAuthResult result)
		{
			result = Parse(uri, false);
			return result != null;
		}

		/// <summary>
		/// Internal method for parsing the VK OAuth url.
		/// </summary>
		/// <param name="uri">
		/// The url to parse.
		/// </param>
		/// <param name="throws">
		/// Whether to throw the exception or not incase an error occurs.
		/// </param>
		/// <returns>
		/// The <see cref="VKOAuthResult"/>.
		/// </returns>
		/// <exception cref="InvalidOperationException">
		/// Throws if cannot parse the specified url.
		/// </exception>
		private static VKOAuthResult Parse(System.Uri uri, bool throws)
		{
			System.Collections.Generic.IDictionary<string, object> parameters = null;

			try
			{
				bool found = false;

				if (!string.IsNullOrEmpty(uri.Fragment))
				{
					var fragment = uri.Fragment.Substring(1);

					parameters = VKUtils.ParseLoginResultString(fragment);

					if (parameters.ContainsKey("access_token"))
						found = true;

					if (parameters.ContainsKey("error_reason") || (parameters.ContainsKey("error") && parameters.ContainsKey("error_description")))
						found = true;
				}

				if (found)
					return new VKOAuthResult(parameters);
			}
			catch
			{
				if (throws)
					throw;

				return null;
			}

			if (throws)
				throw new System.InvalidOperationException("Could not parse authentication url.");

			return null;
		}

	}

}